package com.example.pluginmanager.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.common.eunm.ExceptionCode;
import com.example.pluginmanager.dao.MenuDao;
import com.example.pluginmanager.entity.Menu;
import com.example.pluginmanager.entity.RoleMenuRelation;
import com.example.pluginmanager.entity.User;
import com.example.pluginmanager.interceptor.ValidateJwtTokenInterceptor;
import com.example.pluginmanager.service.MenuService;
import com.example.pluginmanager.service.RoleMenuRelationService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.common.request.PageParam;
import com.common.result.PageVO;
import com.common.util.FieldValidateNotNull;
import com.common.util.IdWorker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static com.common.util.ResponseUtil.returnType;

@Service
public class MenuServiceImpl implements MenuService {

    @Autowired
    private MenuDao menuDao;
    @Autowired
    private IdWorker idWorker;
    @Autowired
    private RoleMenuRelationService roleMenuRelationService;

    @Override
    public Menu getMenuById(String id) {
        Menu menuById = menuDao.getMenuById(id);
        return menuById;
    }

    @Override
    public PageVO<Menu> getMenuByPage(PageParam<Menu> menu) {
        //必填校验
        FieldValidateNotNull.checkRequireParam(menu, Arrays.asList("currentPage", "pageSize"));

        PageHelper.startPage(menu.getCurrentPage(), menu.getPageSize());
        menu.getObj().setStatus(1);
        List<Menu> menuByPage = menuDao.getMenuByPage(menu.getObj());
        Page page = (Page) menuByPage;
        /*
         * 分页返回对象的类型对象
         * */
        PageVO<Menu> menuPageVO = new PageVO<>();
        /*
         * 传到工具类,返回封装分页对象
         * */
        return returnType(menuPageVO, page);
    }

    @Override
    public List<Menu> getAllMenuList(Integer status) {
        Menu menu = new Menu();
        if (status != null) {
            menu.setStatus(status);
        }
        //过滤出与当前用户角色所关联的菜单列表id
        User user = ValidateJwtTokenInterceptor.userThreadLocal.get();
        List<String> roleIds = user.getRoleIds();
        List<RoleMenuRelation> roleMenuRelations = roleMenuRelationService.listRoleMenuByRoleIds(roleIds);
        List<String> menuIdList = roleMenuRelations.stream().map(roleMenuRelation -> roleMenuRelation.getMenuId()).collect(Collectors.toList());
        List<Menu> menuList = getMenuByIds(menuIdList);
        List<Menu> menuTree = disposeMenuList(menuList);
        return menuTree;
    }

    @Override
    public List<Menu> getAllMenuItem() {
        Menu menu1 = new Menu();
        menu1.setStatus(1);
        List<Menu> allMenu = menuDao.getMenuByPage(menu1);
        if (CollUtil.isNotEmpty(allMenu)) {
            List<Menu> allMenuItem = allMenu.stream().filter(menu -> menu.getIsMenuItem() == 1).collect(Collectors.toList());
            return allMenuItem;
        }
        throw new RuntimeException(ExceptionCode.MENU_ITEM_IS_NULL.getMsg());
    }

    @Override
    public Integer disableOrEnableMenuByIds(String[] ids, String status) {

        //禁用
        if (status.equals("0")) {
            //所有菜单(排除禁用)
            Menu menu2 = new Menu();
            menu2.setStatus(1);
            List<Menu> menuByPage = menuDao.getMenuByPage(menu2);
            //判断是否有此次禁用的菜单是否有子菜单 (被禁用的子菜单除外)
            //key: 父组织id  v:此父组织id对应的菜单
            Map<String, List<Menu>> collect = menuByPage.stream().collect(Collectors.groupingBy(Menu::getParentId));
            Arrays.stream(ids).forEach(menuId -> {
                List<Menu> menus = collect.get(menuId);
                if (CollUtil.isNotEmpty(menus)) {
                    Menu menu1 = menuByPage.stream().filter(menu -> menu.getId().equals(menuId)).findFirst().orElse(null);
                    throw new RuntimeException(menu1.getName() + "含有子菜单或菜单项,不能删除");
                }
            });
        } else {
            //启用
            //所有菜单(包括禁用)
            List<Menu> menuByPage = menuDao.getMenuByPage(null);
            HashSet<String> enableIds = new HashSet<>();
            //判断此菜单的父菜单是否启用,如果父菜单未启用,则连带启用
            Arrays.stream(ids).forEach(id -> {
                findEnableMenu(menuByPage, id, enableIds);
            });
            ids = enableIds.toArray(new String[]{});
        }
        Integer integer = menuDao.disableMenuByIds(ids, status);
        return integer;
    }

    @Override
    public Integer modifyMenuById(Menu menu) {
        FieldValidateNotNull.checkRequireParam(menu, Arrays.asList("id"));
        List<Menu> menuList = menuDao.getMenuByPage(null);
        setLevelAndPath(menuList, menu);
        //排除自己
        menuList.forEach(me -> {
            if (me.getId().equals(menu.getId())) {
                menuList.remove(me);
            }
        });
        //判断同等级有相同名称
        judgeGrade(menuList, menu);
        judgeIsMenuItem(menuList, menu);
        Integer row = menuDao.updateMenuById(menu);
        return row;
    }

    @Override
    public Integer addMenu(Menu menu) {
        menu.setId(String.valueOf(idWorker.nextId()));
        List<Menu> menuList = menuDao.getMenuByPage(null);
        setLevelAndPath(menuList, menu);
        //判断同等级有相同名称
        judgeGrade(menuList, menu);
        //判断此次添加的组织的父组织是否是 "菜单",而非"菜单项"
        judgeIsMenuItem(menuList, menu);
        FieldValidateNotNull.checkRequireParam(menu, Arrays.asList("name", "parentId", "routerPath", "isMenuItem"));
        int row = menuDao.insertMenu(menu);
        return row;
    }

    @Override
    public List<Menu> getAllChildrenMenu(String id) {
        List<Menu> allChildrenMenu = menuDao.getAllChildrenMenu(id);
        return allChildrenMenu;
    }

    @Override
    public List<Menu> getMenuByIds(List<String> ids) {
        return menuDao.getMenuByIds(ids);
    }


    private void judgeGrade(List<Menu> menuList, Menu menu) {
        //相同父级的同等级数据
        List<Menu> collect = menuList.stream().filter(me -> me.getParentId().equals(menu.getParentId())).collect(Collectors.toList());
        collect.forEach(coll -> {
            if (coll.getName().equals(menu.getName())) {
                throw new RuntimeException(ExceptionCode.MENU_NAME_REPEAT.getMsg());
            }
        });
    }


    private void setLevelAndPath(List<Menu> menuList, Menu menu) {
        int level = 1;
        Menu newMenu = menu;
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("/" + menu.getId());
        while (true) {
            if ("-1".equals(newMenu.getParentId())) {
                break;
            }
            for (Menu me : menuList) {
                if (newMenu.getParentId().equals(me.getId())) {
                    newMenu = me;
                    stringBuilder.insert(0, "/" + me.getId());
                    level++;
                    break;
                }
            }
        }
        menu.setPath(stringBuilder.toString().trim());
        menu.setLevel(level);
    }

    /*
     * 处理菜单
     * */
    public List<Menu> disposeMenuList(List<Menu> allMenu) {
        //过滤出所有一级菜单
        List<Menu> oneLevel = allMenu.stream().filter(menu -> {
            return menu.getLevel() == 1 && menu.getParentId().equals("-1");
        }).collect(Collectors.toList());

        //从一级菜单向下获取子菜单
        oneLevel.forEach(menu -> {
            getMenuTree(allMenu, menu);
        });

        //菜单已经组装完成,只需要过滤出一级菜单即可 (现在所有的一级菜单下已经挂了对应的子菜单,所以只需要返回一级菜单,
        // 其余的菜单都丢掉)
        List<Menu> menuTree = allMenu.stream().filter(menu -> {
            return menu.getLevel() == 1 && menu.getParentId().equals("-1");
        }).collect(Collectors.toList());
        return menuTree;
    }

    /*
     * 获取菜单树
     * */
    public void getMenuTree(List<Menu> allMenu, Menu menu) {
        //获取当前指定菜单下的所有子菜单
        List<Menu> collect = allMenu.stream().filter(menu1 -> {
            return menu1.getParentId().equals(menu.getId());
        }).collect(Collectors.toList());

        //如果获取到的子菜单个数为0,则return掉此次
        if (collect.size() < 1) {
            return;
        }

        //获取到的子菜单不为0个,则塞入当前这个菜单中
        menu.setChildren(collect);

        //当前菜单的子菜单,进行递归如上操作
        collect.forEach(menu1 -> {
            getMenuTree(allMenu, menu1);
        });
    }

    /*
     * 判断此次添加的组织的父组织是否是 菜单
     * 如果是菜单项,就添加失败
     * */
    public void judgeIsMenuItem(List<Menu> menuList, Menu menu) {
        Menu menu2 = menuList.stream().filter(menu1 -> menu1.getId().equals(menu.getParentId())).findFirst().orElse(null);
        if (menu2 != null && menu2.getIsMenuItem() == 0) {
            return;
        } else {
            throw new RuntimeException(ExceptionCode.MENU_ITEM_ADD_EXCE.getMsg());
        }
    }

    /*
     * 递归判断父级菜单是否启用,未启用则加入到启用的集合中,一并启用
     * */
    private void findEnableMenu(List<Menu> menus, String menuId, Set<String> menuIds) {
        Map<String, Menu> collect = menus.stream().collect(Collectors.toMap(Menu::getId, menuV -> menuV));
        while (menuId != null && (!menuId.equals("-1"))) {
            Menu menu = collect.get(menuId);
            menuId = menu.getParentId();
            if (menuId != null) {
                if (menu.getStatus() == 0) {
                    //内存中的菜单数据改为启用,下一次外循环的时候就不用判断这个父级菜单了
                    menu.setStatus(1);
                    menuIds.add(menu.getId());
                }
            } else {
                break;
            }
        }
    }
}
